package torres.entity;

import java.io.Serializable;

import torres.item.*;
import torres.map.Location;
import torres.map.Map;
import torres.map.World;

/**
 * @author Torres
 * An entity is a mobile thing which has a specific location and the 
 * ability to relocate itself. Entities are characterized by stats.
 * Can be either player-controlled or computer-controlled.
 */
@SuppressWarnings("serial")
public class Entity implements Serializable{
	private String name;
	private String fullName;
	private int currLife;
	private int currMana;
	private int livesLeft;
	private int intellect;
	private int strength;
	private int agility;
	private int hardiness;
	private int experience;
	private int movement;
	private int level;
	private Location location;
	private String imageName;
	private String direction;
	
	private Map map;
	private Occupation job;
	
	private Inventory inventory;
	private EquippedInventory equipInventory;
	//private World world = World.getInstance();
	
	public Entity () {
		
	}
	
	/**
	 * Main constructor that creates the Entity instance.
	 * @param name the name of the entity
	 * @param job the occupation of the entity
	 */
	public Entity (String name, Occupation job) {
		livesLeft = 3;
		level = 1;
		experience = 0;
		this.job = job;
		this.setHardiness((int)(10 * this.getOccupation().getStrengthMultiplier()));
		this.setIntellect((int)(10 * this.getOccupation().getIntellectMultiplier()));
		this.setStrength((int)(10 * this.getOccupation().getStrengthMultiplier()));
		this.setAgility((int)(10 * this.getOccupation().getAgilityMultiplier()));
		this.setMovement((int)(5 * this.getOccupation().getAgilityMultiplier()));
		this.setCurrLife(getMaxLife());
		this.setCurrMana(getMaxMana());
		inventory = new Inventory();
		equipInventory = new EquippedInventory();
		this.setName(name);
		map = World.getInstance().getCurrentMap();
		direction = "E";
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		return "Entity's name is: " + name + " currlife = " + currLife;
	}
	
	/*Accessor methods for basic stats*/
	
	/**
	 * Sets the name of the Entity 
	 * @param name name as String of Entity
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the name of the entity
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * @param fullName the full name of the entity
	 */
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}
	
	/**
	 * @return fullname
	 */
	public String getFullName() {
		return fullName;
	}
	
	/**
	 * @return Inventory
	 */
	public Inventory getInventory() {
		return inventory;
	}
	
	/**
	 * @return EquippedInventory
	 */
	public EquippedInventory getEquippedInventory() {
		return equipInventory;
	}

	/**
	 * @return the Intellect of the entity
	 */
	public int getIntellect(){
		return intellect;
	}
	
	/**
	 * Sets the intellect of this entity
	 * @param intellect int value of the intellect
	 */
	public void setIntellect(int intellect){
		this.intellect = intellect;
	}
	
	public int getStrength(){
		return strength;
	}
	
	public void setStrength(int strength){
		this.strength = strength;
	}
	
	public int getAgility() {
		return agility;
	}
	
	public void setAgility(int agility) {
		this.agility = agility;
	}
	
	public int getHardiness() {
		return hardiness;
	}
	
	public void setHardiness(int hardiness) {
		this.hardiness = hardiness;
	}
	
	public int getExperience() {
		return experience;
	}
	
	public int getMovement() {
		return movement;
	}
	
	public void setMovement(int movement) {
		this.movement = movement;
	}
	public void setExperience(int experience) {
		this.experience = experience;
	}
	
	public Occupation getOccupation() {
		return job;
	}
	
	/*Accessor methods for life and mana*/
	
	public int getLivesLeft() {
		return livesLeft;
	}
	
	public void setLivesLeft(int livesLeft) {
		if(livesLeft<0)
			this.livesLeft = 0;
		else
			this.livesLeft = livesLeft;
	}
	
	public int getCurrLife(){
		return currLife;
	}
	
	public void setCurrLife(int currLife){
		if(currLife<=0) {
			this.currLife = this.getMaxLife();
			setLivesLeft(getLivesLeft()-1);
		}
		else if(currLife > this.getMaxLife())
			this.currLife = this.getMaxLife();
		else
			this.currLife = currLife;
	}
	
	public int getCurrMana(){
		return currMana;
	}
	
	public void setCurrMana(int mana){
		if(mana < 0)
			this.currMana = 0;
		else if(mana > this.getMaxMana())
			this.currMana = this.getMaxMana();
		else
			this.currMana = mana;
	}
	
	public int getLevel(){
		return level;
	}
	
	public void setLevel(int level) {
		this.level = level;
	}
	public Location getLocation() {
		return location;
	}
	
	public void setLocation(Location location) {
		this.location=location;
	}
	
	
	public void setImageName(String imageName) {
		this.imageName = imageName;
	}

	public String getImageName() {
		return imageName;
	}
	
	/*Derived stats accessors. These may need something to accomodate 
	 increased derived stat items*/

	public int getMaxLife() {
		return (int) (1.5*level+1.2*hardiness);
	}
	
	public int getMaxMana() {
		return (int) (1.5*level + Math.sqrt(intellect));
	}
	
	public int getOffensiveRating() {
		return (int) (1.5*level + 1.2*strength) + equipInventory.getWeaponTotal();
	}
	
	public int getDefensiveRating() {
		return (int) (1.5*level + 1.2*agility);
	}
	
	public int getArmorRating() {
		return (int) (1.2*hardiness) + equipInventory.getArmorTotal();
	}
	
	public void decCurrLife(int amount) {
		int armorRating = this.getArmorRating();
		armorRating /= 10;
		amount -= armorRating;
		this.setCurrLife(this.getCurrLife() - amount);
	}
	
	public void incCurrLife(int amount) {
		this.setCurrLife(this.getCurrLife() + amount);
		
	}
	
	public void decCurrMana(int amount) {
		this.setCurrMana( this.getCurrMana() - amount);
	}
	
	public void incCurrMana(int amount) {
		this.setCurrMana( this.getCurrMana() + amount);
	}
	
	public void incExp(int amount) {
		experience += amount;
	}
	
	public boolean addItem(TakeableItem item) {
		return inventory.addItem(item);
	}
	
	public TakeableItem removeItem(TakeableItem item) {
		return inventory.removeItem(item);
	}
	
	public void equipItem(EquippableItem item) {
		if(equipInventory.addItem(item))
			inventory.removeItem(item);
	}
	
	public EquippableItem unEquipItem(EquippableItem item) {
		if(!inventory.isFull()) {
			inventory.addItem(item);
			equipInventory.removeItem(item);
			return item;
		}
		else
			return null;
	}
	
	public void updateCurrentMap() {
		map = World.getInstance().getCurrentMap();
	}
	
	public boolean hasItem(TakeableItem item) {
		return (inventory.hasItem(item) || equipInventory.hasItem(item));
	}
	
	public String getDirection() {
		
		return direction;
		
	}
	
	public void setDirection(Location oldLocation, Location location) {
		int oX = oldLocation.getX();
		int oY = oldLocation.getY();
		int x = location.getX();
		int y = location.getY();
		if(((x-oX)>0) && ((y-oY)==0))
			direction = "E";
		else if(((x-oX)>0) && ((y-oY)<0))
			direction = "E";
		else if(((x-oX)==0) && ((y-oY)<0))
			direction = "N";
		else if(((x-oX)<0) && ((y-oY)<0))
			direction = "W";
		else if(((x-oX)<0) && ((y-oY)==0))
			direction = "W";
		else if(((x-oX)<0) && ((y-oY)>0))
			direction = "W";
		else if(((x-oX)==0) && ((y-oY)>0))
			direction = "S";
		else if(((x-oX)>0) && ((y-oY)>0))
			direction = "E";
	}
	
	/*public boolean hasItem(TakeableItem item) {
		if(inventory.getItemList().indexOf(item)!=-1 || equipInventory.)
			return true;
		else
			return false;
	}*/
	
	/*Updates level, called whenever a tile is stepped
	  on that allows you to level up*/
	public void updateLevel(){
		int currLevel = level;
		int newLevel = (int) (Math.pow(experience,(1/2.5))+1);
		int levelDiff = newLevel - currLevel;
		if( levelDiff > 0 ) {
			intellect = (int) (intellect*job.getIntellectMultiplier()*levelDiff);
			strength = (int) (strength*job.getStrengthMultiplier()*levelDiff);
			agility = (int) (agility*job.getAgilityMultiplier()*levelDiff);
			this.setLevel(newLevel);
			this.currLife = getMaxLife();
			this.currMana = getMaxMana();
		}
	}
	
	public boolean move(Location location) {
		/*switch(direction) {
		case NORTH: location.setY(location.getY()-1);
		case NORTHWEST: location.setY(location.getY()-1); location.setX(location.getX()-1); break;
		case WEST: location.setX(location.getX()-1); break;
		case SOUTHWEST: location.setY(location.getY()+1); location.setX(location.getX()-1); break;
		case SOUTH: location.setY(location.getY()+1); break;
		case SOUTHEAST: location.setY(location.getY()+1); location.setX(getX()+1); break;
		case EAST: location.setX(location.getX()+1); break;
		case NORTHEAST: location.setY(location.getY()-1); location.setX(location.getX()+1); break;
		}
		*/
		updateCurrentMap();
		
		Location oldLocation = this.getLocation();
		setDirection(oldLocation, location);
		if(map.isValid(location) && !map.hasObstacle(location) && map.isTraversable(location)){
			if(map.hasItem(location)) {
				Item currItem;
				currItem = map.getItem(location);
				if(this.getOccupation().getJob() == Occupation.Job.SNEAK && currItem.isTrap()) {
					map.getTile(location).getItem().setConsumed(true);
				}
				currItem.onTouch(this);

				if(currItem.isConsumed()) {
					map.removeItem(location);
				}
			}
			/*if(map.hasEffect(location)) {
				AreaEffect effect = map.getEffect(location);
				effect.applyEffect(this);
			}*/
			if(this.getLocation()==oldLocation)
				this.location = location;
			return true;
		}
		else
			return false;
	}
	
}